define(async () => {
    const util = await require("../util");
    const Animator = await require("Animator");
    const Animation = await require("Animation");
    const EventEmitter = await require("EventEmitter");

    /**
     * 节点类
     * 
     * 几乎为大部分元素的基类，它描述了元素在画布中基本的状态
     */
     class Node extends EventEmitter {

        id;  //元素ID
        x;  //X轴坐标
        y;  //Y轴坐标
        width;  //元素宽度
        height;  //元素高度
        rotate;  //元素旋转角度
        zIndex;  //元素层级
        rotateX;  //元素旋转圆心X轴坐标
        rotateY;  //元素旋转圆心Y轴坐标
        originX;  //元素原点X轴坐标
        originY;  //元素原点Y轴坐标
        scale;  //元素等比缩放值
        scaleX;  //元素X轴缩放值
        scaleY;  //元素Y轴缩放值
        offsetAngle;  //元素旋转角度偏移，一般用于统一元素方向
        animator;  //绑定的元素动画生成器
        childrens = [];  //节点的下级子节点，后续更新
        parent;  //父级指向
        layer;  //挂载的图层
        _updateCallback;  //更新监听回调

        constructor(options = {}) {
            super();
            util.assert(util.isObject(options), new TypeError("options must be an Object"));
            const { id, x, y, width, height, rotate, zIndex, rotateX, rotateY, originX, originY, scale, scaleX, scaleY, offsetAngle } = options;
            util.assert(util.isUndefined(x) || util.isNumber(x), new TypeError("x must be an Number"));
            util.assert(util.isUndefined(y) || util.isNumber(y), new TypeError("y must be an Number"));
            util.assert(util.isNumber(width), new TypeError("width must be an Number"));
            util.assert(util.isNumber(height), new TypeError("height must be an Number"));
            util.assert(util.isUndefined(rotate) || util.isNumber(rotate), new TypeError("rotate must be an Number"));
            util.assert(util.isUndefined(zIndex) || util.isNumber(zIndex), new TypeError("zIndex must be an Number"));
            util.assert(util.isUndefined(rotateX) || util.isNumber(rotateX), new TypeError("rotateX must be an Number"));
            util.assert(util.isUndefined(rotateY) || util.isNumber(rotateY), new TypeError("rotateY must be an Number"));
            util.assert(util.isUndefined(originX) || util.isNumber(originX), new TypeError("originX must be an Number"));
            util.assert(util.isUndefined(originY) || util.isNumber(originY), new TypeError("originY must be an Number"));
            util.assert(util.isUndefined(scale) || util.isNumber(scale), new TypeError("scale must be an Number"));
            util.assert(util.isUndefined(scale) || util.inRange(scale, 0.5, 2.1), new TypeError("scale can only in the range of 0.5~2"))
            util.assert(util.isUndefined(scaleX) || util.isNumber(scaleX), new TypeError("scaleX must be an Number"));
            util.assert(util.isUndefined(scaleY) || util.isNumber(scaleY), new TypeError("scaleY must be an Number"));
            util.assert(util.isUndefined(offsetAngle) || util.isNumber(offsetAngle), new TypeError("offsetAngle must be an Number"));
            this.id = util.defaultTo(id, util.uniqueId("node_"));  //如果不提供ID则使用随机的ID
            this.x = util.defaultTo(x, 0);
            this.y = util.defaultTo(y, 0);
            this.width = width;
            this.height = height;
            this.rotate = util.defaultTo(rotate, 0);
            this.zIndex = util.defaultTo(zIndex, 0);
            this.rotateX = util.defaultTo(rotateX, 0);
            this.rotateY = util.defaultTo(rotateY, 0);
            this.originX = util.defaultTo(originX, 0);
            this.originY = util.defaultTo(originY, 0);
            this.scaleX = util.defaultTo(scaleX, util.defaultTo(scale, 1));
            this.scaleY = util.defaultTo(scaleY, util.defaultTo(scale, 1));
            this.offsetAngle = util.defaultTo(offsetAngle, 0);
            this.animator = new Animator();
            this.animator.parent = this;
        }

        setAnimationLoop(value) {
            util.assert(util.isBoolean(value), new TypeError("animationLoop must be an Boolean"));
            this.animator.loop = value;
            return this;
        }

        setAnimationTimingFunction(value) {
            util.assert(util.isString(value), new TypeError("timingFunction must be an String"));
            this.animator.timingFunction = value;
            return this;
        }

        /**
         * 打断动画
         * 
         * @return {Node} 节点实例
         */
        interruptAnimation() {
            this.animator.interruptAnimation();
            return this;
        }

        /**
         * 元素动画
         * 
         * @param {Object} target 目标
         * @return {Node} 节点实例
         */
        animation(target) {
            util.assert(util.isObject(target), new TypeError("target must be an Object"));
            const { x, y, rotate, scale, scaleX, scaleY, duration, timingFunction } = target;
            const animation = new Animation({
                targetX: x,
                targetY: y,
                targetAngle: rotate,
                targetScale: scale,
                targetScaleX: scaleX,
                targetScaleY: scaleY,
                duration,
                timingFunction
            });
            this.animator.addAnimation(animation);
            return this;
        }

        /**
         * 添加多个元素动画
         * 
         * @param {Object[]} targets 目标集合
         * @param {Object} options 选项
         * @returns 
         */
        animations(...targets) {
            const animations = targets.map(target => {
                util.assert(util.isObject(target), new TypeError("target must be an Object"));
                const { x, y, rotate, scale, scaleX, scaleY, duration, timingFunction } = target;
                return new Animation({
                    targetX: x,
                    targetY: y,
                    targetAngle: rotate,
                    targetScale: scale,
                    targetScaleX: scaleX,
                    targetScaleY: scaleY,
                    duration,
                    timingFunction
                });
            });
            this.animator.addAnimations(...animations);
            return this;
        }

        /**
         * 渲染节点
         */
        render() {
            //如果节点未在视窗内则无需处理它的脏矩形
            if(this.inViewport)
                this.layer.addDirtyRect(this.renderX - this.rotateX, this.renderY - this.rotateY, (this.width + this.rotateX * 2) * this.scaleX, (this.height + this.rotateY * 2) * this.scaleY);  //添加脏矩形
            this.emit("render");
            this.animator.update();
        }

        /**
         * 恢复节点动画
         */
        resume() {
            if(this.animator)
                this.animator.resume();
        }

        /**
         * 暂停节点动画
         */
        pause() {
            if(this.animator)
                this.animator.pause();
        }

        /**
         * 销毁节点
         * 
         * 将节点从父级中移除
         */
        destory() {
            this.parent.removeChild(this);
        }

        get fps() {
            return this.parent.fps;
        }

        /**
         * 判断当前节点是否处于视窗内
         */
        get inViewport() {
            const diagonalLengthHalf = Math.sqrt(Math.pow(this.width * this.scaleX, 2) + Math.pow(this.height * this.scaleY, 2)) / 2;  //计算节点对角线长度的一半
            return this.x - diagonalLengthHalf > this.layer.width || this.y - diagonalLengthHalf > this.layer.height || this.x + diagonalLengthHalf < 0 || this.y + diagonalLengthHalf < 0 ? false : true;
        }

        /**
         * 获得渲染真正的X轴坐标
         */
        get renderX() {
            return this.x - this.originX - (this.width * this.scaleX - this.width) / 2;
        }

        /**
         * 获得渲染真正的Y轴坐标
         */
        get renderY() {
            return this.y - this.originY - (this.height * this.scaleY - this.height) / 2;
        }

        /**
         * 判断是否Node实例
         * 
         * @param {Any} value 
         * @return {Boolean}
         */
        static isInstance(value) {
            return value instanceof Node;
        }

    }

    return Node;
});